home *** CD-ROM | disk | FTP | other *** search
/ Qu.......ke Neue Level / KroGer Software GmbH - Qu_ke.iso / UTILITY / PRG8.ZIP / F_PACK.C < prev    next >
C/C++ Source or Header  |  1996-03-01  |  10KB  |  308 lines

  1. /*
  2.  * Copyright (C) 1996 by Raphael Quinet.  All rights reserved.
  3.  *
  4.  * Permission to use, copy, modify, and distribute this software and
  5.  * its documentation for any purpose and without fee is hereby
  6.  * granted, provided that the above copyright notice appear in all
  7.  * copies and that both that copyright notice and this permission
  8.  * notice appear in supporting documentation.  If more than a few
  9.  * lines of this code are used in a program which displays a copyright
  10.  * notice or credit notice, the following acknowledgment must also be
  11.  * displayed on the same screen: "This product includes software
  12.  * developed by Raphael Quinet for use in the Quake Editing Utilities
  13.  * project."  THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR
  14.  * IMPLIED WARRANTY.
  15.  *
  16.  * More information about the QEU project can be found on the WWW:
  17.  * "http://www.montefiore.ulg.ac.be/~quinet/games/editing.html" or by
  18.  * mail: Raphael Quinet, 9 rue des Martyrs, B-4550 Nandrin, Belgium.
  19.  */
  20.  
  21. /*
  22.  * F_PACK.C - Read and write Quake PACK files.
  23.  */
  24.  
  25. #include "qeu.h"
  26. #include "q_misc.h"
  27. #include "q_files.h"
  28. #include "f_pack.h"
  29.  
  30. /*
  31.  * Read the PACK directory into memory.  The optional offset to the
  32.  * start of the PACK file is given in "offset".  The number of files in
  33.  * the directory is returned in *dirsize_r.
  34.  */
  35. PACKDirPtr ReadPACKDirectory(FILE *packfile, UInt32 offset, UInt16 *dirsize_r)
  36. {
  37.   PACKDirPtr dir;
  38.   UInt32     pos, size;
  39.   UInt16     max, i;
  40.  
  41.   *dirsize_r = 0;
  42.   if (packfile == NULL)
  43.     return NULL;
  44.   if ((fseek(packfile, offset, SEEK_SET) < 0)
  45.       || (ReadMagic(packfile) != FTYPE_PACK)
  46.       || (ReadInt32(packfile, &pos) == FALSE)
  47.       || (ReadInt32(packfile, &size) == FALSE)
  48.       || (size == 0L)
  49.       || (size / sizeof(struct PACKDirectory) > 65535L)
  50.       || (fseek(packfile, offset + pos, SEEK_SET) < 0))
  51.     return NULL;
  52.   dir = (PACKDirPtr)QMalloc(size);
  53.   max = (UInt16)(size / sizeof(struct PACKDirectory));
  54.   for (i = 0; i < max; i++)
  55.     {
  56.       if (ReadBytes(packfile, &dir[i], sizeof(struct PACKDirectory)) == FALSE)
  57.     {
  58.       QFree(dir);
  59.       return NULL;
  60.     }
  61.       ConvertFilePath(dir[i].name);
  62.       dir[i].offset = SwapInt32(dir[i].offset);
  63.       dir[i].size = SwapInt32(dir[i].size);
  64.     }
  65.   *dirsize_r = max;
  66.   return dir;
  67. }
  68.  
  69.  
  70. /*
  71.  * Return the index number of the first entry matching "entryname".
  72.  * If "entryname" contains no directory separators ("/" or "\"), then
  73.  * try also to match it with the base name of all entries.  A number
  74.  * greater or equal to dirsize is returned if no match is found.
  75.  */
  76. UInt16 FindPACKEntry(PACKDirPtr dir, UInt16 dirsize, char *entryname)
  77. {
  78.   UInt16 i;
  79.   char   *p;
  80.  
  81.   if (dir == NULL)
  82.     ProgError("BUG: Cannot find entry in NULL directory");
  83.   /* first, look for the full name */
  84.   for (i = 0; i < dirsize; i++)
  85.     if (!strnicmp(dir[i].name, entryname, 56))
  86.       return i;
  87.   /* then look for the base name (if entryname contains no "/" or "\") */
  88.   for (p = entryname; *p; p++)
  89.     if (*p == '/' || *p == '\\')
  90.       return i;
  91.   for (i = 0; i < dirsize; i++)
  92.     if (!strnicmp(GetBaseName(dir[i].name), entryname, 56))
  93.       return i;
  94.   return i;
  95. }
  96.  
  97.  
  98. /*
  99.  * Print the contents of the PACK directory in "outf".
  100.  */
  101. void DumpPACKDirectory(FILE *outf, PACKDirPtr dir, UInt16 dirsize)
  102. {
  103.   UInt16 i;
  104.   UInt32 sum;
  105.   char   buf[57];
  106.  
  107.   if (outf == NULL || dir == NULL || dirsize == 0)
  108.     return;
  109.   fprintf(outf, "num    offset     size    file name\n");
  110.   fprintf(outf, "       (hex)      (dec)\n");
  111.   sum = 0L;
  112.   for (i = 0; i < dirsize; i++)
  113.     {
  114.       strncpy(buf, dir[i].name, 56);
  115.       buf[56] = '\0';
  116.       fprintf(outf, "%3u  0x%08lx  %6ld   %s\n",
  117.           i, dir[i].offset, dir[i].size, buf);
  118.       sum += dir[i].size;
  119.     }
  120.   fprintf(outf, "\nTotal size for %3u entries:  %7lu bytes.\n", dirsize, sum);
  121.   fprintf(outf, "Size of the PACK directory:  %7lu bytes.\n",
  122.       (UInt32)dirsize * (UInt32)sizeof(struct PACKDirectory));
  123.   fprintf(outf, "Total (header + data + dir): %7lu bytes.\n",
  124.       12L + sum + (UInt32)dirsize * (UInt32)sizeof(struct PACKDirectory));
  125. }
  126.  
  127.  
  128. /*
  129.  * If "entrynum" is smaller than dirsize, extract the corresponding
  130.  * entry from a PACK file.  Otherwise, extract all entries and save
  131.  * them in separate files.  The files will be saved in the directory
  132.  * "prefixpath".  If "outf" is not null, progress information will be
  133.  * printed in it.
  134.  */
  135. Bool UnPACKFile(FILE *outf, FILE *packfile, UInt32 offset, PACKDirPtr dir,
  136.         UInt16 dirsize, UInt16 entrynum, char *prefixpath)
  137. {
  138.   char   *newname;
  139.   char   *p;
  140.   FILE   *newfile;
  141.   FILE   *indexfile;
  142.   UInt16  i;
  143.   UInt32  sum;
  144.  
  145.   if (packfile == NULL || dir == NULL || dirsize == 0)
  146.     return FALSE;
  147.   if (prefixpath == NULL)
  148.     prefixpath = ".";
  149.   newname = (char *)QMalloc(strlen(prefixpath) + 56 + 2);
  150.   strcpy(newname, prefixpath);
  151.   p = &newname[strlen(newname) - 1];
  152. #ifdef QEU_DOS
  153.   if (*p != '\\')
  154.     {
  155.       p++;
  156.       *p = '\\';
  157.     }
  158. #else
  159.   if (*p != '/')
  160.     {
  161.       p++;
  162.       *p = '/';
  163.     }
  164. #endif
  165.   p++;
  166.   strcpy(p, QEU_INDEX_FILE);
  167.   if (outf != NULL)
  168.     fprintf(outf, "Creating index file %s\n", newname);
  169.   CreatePathToFile(newname);
  170.   indexfile = fopen(newname, "a");
  171.   fprintf(indexfile, "BEGIN PACK\n");
  172.   sum = 0L;
  173.   for (i = 0; i < dirsize; i++)
  174.     {
  175.       if (entrynum < dirsize && i != entrynum)
  176.     continue; /* horrible trick... */
  177.       strcpy(p, dir[i].name);
  178.       if (outf != NULL)
  179.     fprintf(outf, "Saving %6ld bytes to %s\n", dir[i].size, newname);
  180.       CreatePathToFile(newname);
  181.       newfile = fopen(newname, "wb");
  182.       if (newfile == NULL)
  183.     {
  184.       fclose(indexfile);
  185.       QFree(newname);
  186.       return FALSE;
  187.     }
  188.       fprintf(indexfile, "+ %s = %s\n", p, p);
  189.       if ((fseek(packfile, offset + dir[i].offset, SEEK_SET) < 0)
  190.       || (CopyBytes(newfile, packfile, dir[i].size) == FALSE))
  191.     {
  192.       fclose(newfile);
  193.       fclose(indexfile);
  194.       QFree(newname);
  195.       return FALSE;
  196.     }
  197.       fclose(newfile);
  198.       sum += dir[i].size;
  199.     }
  200.   if (outf != NULL && entrynum >= dirsize)
  201.     fprintf(outf, "Saved %lu bytes in %u files.\n", sum, dirsize);
  202.   fprintf(indexfile, "END PACK\n");
  203.   fclose(indexfile);
  204.   QFree(newname);
  205.   return TRUE;
  206. }
  207.  
  208.  
  209. /* ----------------------------------------------------------------------------
  210.  * NOTE: How to save a pack file:
  211.  *
  212.  * UInt32     count;  - used by the saving routines
  213.  * PACKDirPtr dir;    - PACK directory structure (created step by step)
  214.  * UInt16     n;      - number of entries in PACK directory (idem)
  215.  *
  216.  * WritePACKHeader(f, &count, &dir, &n);            - write the header
  217.  * size = WriteSomething(f, ...);                   - save one entry
  218.  * AddPackEntry(f, &count, &dir, &n, name, size);   - add this entry to dir.
  219.  * size = WriteSomethingElse(f, ...);               - save another entry
  220.  * AddPackEntry(f, &count, &dir, &n, othername, size); - add it to dir. too
  221.  * totalsize = WritePACKDirectory(f, &count, dir, n);  - write the directory
  222.  */
  223.  
  224. /*
  225.  * Write the PACK header to the file.  The header will be modified later,
  226.  * when the directory is written to the file.
  227.  */
  228. Bool WritePACKHeader(FILE *packfile, UInt32 *count_r, PACKDirPtr *dir_r,
  229.              UInt16 *dirsize_r)
  230. {
  231.   char buf[100];
  232.  
  233.   sprintf(buf, "PACK********\r\nQEU %s\r\n", QEU_VERSION);
  234.   if ((packfile == NULL)
  235.       || (WriteBytes(packfile, buf, (UInt32)strlen(buf)) == FALSE))
  236.     return FALSE;
  237.   *dir_r = NULL;
  238.   *count_r = (UInt32)strlen(buf);
  239.   *dirsize_r = 0;
  240.   return TRUE;
  241. }
  242.  
  243.  
  244. /*
  245.  * Add a new entry to the PACK directory.  This entry should have been
  246.  * saved previously and be "entrysize" bytes long.  It will be stored
  247.  * in the PACK directory under the name "entryname".
  248.  * All object saving routines in this package return the number of bytes
  249.  * written, so that number can be passed directly to this routine.
  250.  */
  251. Bool AddPACKEntry(FILE *packfile, UInt32 *count_r, PACKDirPtr *dir_r,
  252.           UInt16 *dirsize_r, char *entryname, UInt32 entrysize)
  253. {
  254.   UInt16 n;
  255.  
  256.   if (packfile == NULL || *count_r == 0L)
  257.     return FALSE;
  258.   n = *dirsize_r;
  259.   if (n == 0)
  260.     *dir_r = (PACKDirPtr)QMalloc((UInt32)sizeof(struct PACKDirectory));
  261.   else
  262.     *dir_r = (PACKDirPtr)QRealloc(*dir_r, (UInt32)(n + 1)
  263.                   * (UInt32)sizeof(struct PACKDirectory));
  264.   QStrNCpy((*dir_r)[n].name, entryname, 56);
  265.   (*dir_r)[n].offset = *count_r;
  266.   (*dir_r)[n].size = entrysize;
  267.   *count_r = *count_r + entrysize;
  268.   *dirsize_r = n + 1;
  269.   return TRUE;
  270. }
  271.  
  272.  
  273. /*
  274.  * Write the PACK directory to the file.  This should only be done
  275.  * after all entries have been saved (using the appropriate
  276.  * Write... routine) and registered (using AddPACKEntry).  The PACK
  277.  * header is updated so that it points to the directory.
  278.  *
  279.  * This routine returns the total number of bytes taken by the PACK
  280.  * file (header + all entries + directory).  It is thus possible to
  281.  * include a PACK file in another PACK file or in a WAD file.
  282.  */
  283. UInt32 WritePACKDirectory(FILE *packfile, UInt32 *count_r, PACKDirPtr dir,
  284.               UInt16 dirsize)
  285. {
  286.   UInt32 size, pos;
  287.   UInt16 i;
  288.  
  289.   pos = *count_r;
  290.   if (packfile == NULL || pos == 0L)
  291.     return 0L;
  292.   *count_r = 0L; /* invalidate the counter */
  293.   size = (UInt32)dirsize * (UInt32)sizeof(struct PACKDirectory);
  294.   if ((fseek(packfile, 4L - (Int32)(pos), SEEK_CUR) < 0)
  295.       || (WriteInt32(packfile, &pos) == FALSE)
  296.       || (WriteInt32(packfile, &size) == FALSE)
  297.       || (fseek(packfile, (Int32)(pos) - 12L, SEEK_CUR) < 0))
  298.     return 0L;
  299.   for (i = 0; i < dirsize; i++)
  300.     if ((WriteBytes(packfile, &dir[i], 56) == FALSE)
  301.     || (WriteInt32(packfile, &(dir[i].offset)) == FALSE)
  302.     || (WriteInt32(packfile, &(dir[i].size)) == FALSE))
  303.       return 0L;
  304.   return size + pos;
  305. }
  306.  
  307. /* end of file */
  308.